(function(){
    // util function
    function getShader(gl, shaderScript){
        var str = "";
        var k = shaderScript.firstChild;
        while (k) {
            if (k.nodeType == 3) {
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
            shader = gl.createShader(gl.FRAGMENT_SHADER);
        } else if (shaderScript.type == "x-shader/x-vertex") {
            shader = gl.createShader(gl.VERTEX_SHADER);
        } else {
            throw "invalid shader script type"
        }

        gl.shaderSource(shader, str);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            throw gl.getShaderInfoLog(shader);
        }

        return shader
    };

    function exportShaderParam(gl, shaderProgram, type, exportParam){
        buf = {};
        $.each(exportParam, function(i, p){
            switch(p[0]){
                case "a":{
                    buf[p] = gl.getAttribLocation(shaderProgram, p);
                    gl.enableVertexAttribArray(buf[p]);
                    break;
                }
                case "u":{
                    buf[p] = gl.getUniformLocation(shaderProgram, p);
                    break;
                }
            }
        });

        shaderProgram[type+"Param"] = buf;
    };

    // engine class
    MyWebGL = function(canvas, vsScript, fsScript, vsParam, fsParam){
        // private var
        var _this = this;
        var gl = null;
        var shaderProgram = null;

        // public var
        this.viewWidth = null;
        this.viewHeight = null;
        
        // public function
        this.bufferVertex = function(data, paramName, size){
            var buffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
            gl.vertexAttribPointer(shaderProgram.vParam[paramName], size, gl.FLOAT, false, 0, 0);
        };

        this.uniformMatrix = function(matrixArray, paramName){
            gl.uniformMatrix4fv(shaderProgram.vParam[paramName], false, matrixArray);
        };

        this.draw = function(){
            gl.drawArrays(gl.TRIANGLES, 0, 3);
        };

        // do init
        (function init(){
            // init context
            try{
                gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
            }catch(e){}

            if(!gl){
                throw "Unable to initialize WebGL. Your browser may not support it."
            }

            _this.viewWidth = canvas.width;
            _this.viewHeight = canvas.height;

            // init shader
            shaderProgram = gl.createProgram();
            gl.attachShader(shaderProgram, getShader(gl, vsScript));
            gl.attachShader(shaderProgram, getShader(gl, fsScript));

            gl.linkProgram(shaderProgram);
            if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                throw "Could not initialise shaders";
            }

            gl.useProgram(shaderProgram);
            exportShaderParam(gl, shaderProgram, "v", vsParam);
            exportShaderParam(gl, shaderProgram, "f", fsParam);

            // init screen
            gl.clearColor(0.0, 0.0, 0.0, 1.0); 
            gl.enable(gl.DEPTH_TEST);
            gl.viewport(0, 0, _this.viewWidth, _this.viewHeight);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        })();
    };
})();
